﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Tcp;
using UtilZ.Dotnet.Ex.Log;
using UtilZ.Dotnet.Ex.Model;

namespace ConsoleApp.Test
{
    internal class TestNetTcpClient : UnpackerAbs
    {
        private readonly UnpackerUseMode _useMode;
        private TestNetTcpClient(UnpackerUseMode useMode)
        {
            this._useMode = useMode;
        }

        public static void Test()
        {
            GenerateSendBuffer();

            var initPara = new NetTcpListenerInitPara(28001, 3)
            {

            };
            var tcpListener = new NetTcpListener(AcceptSocket, initPara);
            tcpListener.Start();

            Console.ReadKey();
        }

        private static NetTcpClient _tcpClient = null;
        private static void AcceptSocket(AcceptSocketArgs args)
        {
            if (_tcpClient != null)
            {
                Free();
            }

            var unpacker = new TestNetTcpClient(UnpackerUseMode.Monopolize);
            unpacker.PackOutput += Unpacker_PackOutput;
            var initPara = new NetTcpClientInitPara(unpacker, args)
            {
                BufferSize = 30,
                BufferSizeLimit = 100,
                ReceiveDataSize = 20,
                ReceiveDataMode = ThreadMode.Sync
            };
            _tcpClient = new NetTcpClient(initPara);
            _tcpClient.ConnectChanged += _tcpClient_ConnectChanged;
            Loger.Info("客户端连接建立");
        }

        private static void Free()
        {
            _tcpClient.ConnectChanged -= _tcpClient_ConnectChanged;
            _tcpClient.InitPara.Unpacker.PackOutput -= Unpacker_PackOutput;
            _tcpClient.Dispose();
            _tcpClient = null;
        }

        private static void _tcpClient_ConnectChanged(object sender, NetConnectedChangedArgs e)
        {
            if (e.Status == ConnectionStatus.Closed)
            {
                Free();
                Loger.Warn("客户端连接断开");
            }
        }

        private static void Unpacker_PackOutput(object sender, DataOutputArgs e)
        {
            Loger.Info($"收到数据长度:{e.Data.Length}");
        }


        private const uint DATA_PACK_HEAD_SYNC = 0xF99FEFFE;
        private const int DATA_PACK_HEAD_SIZE = 8;
        private DataSource _bufferData = null;
        private DataOutputArgs _args = null;

        protected override int PrimitiveUnPackge(IUnpackerPara unpackerPara)
        {
            var tcpUnpackerPara = (NetTcpUnpackerPara)unpackerPara;

            var reader = tcpUnpackerPara.Reader;
            Stream stream = reader.BaseStream;
            long parseBeginPosition = stream.Position;
            long currentParseBeginPosition = parseBeginPosition;
            long parseEndPosition = parseBeginPosition + tcpUnpackerPara.DataLength;
            int parseDataLength = 0;

            if (parseEndPosition - currentParseBeginPosition < DATA_PACK_HEAD_SIZE)
            {
                return parseDataLength;
            }

            long dataPackageEndPos;
            DataOutputArgs args;
            while (stream.Position < parseEndPosition &&
                parseEndPosition - stream.Position > DATA_PACK_HEAD_SIZE &&
                !unpackerPara.Client.Disposed)
            {
                var sync = reader.ReadUInt32();
                if (sync != DATA_PACK_HEAD_SYNC)
                {
                    string hexStr = ConvertEx.BytesToHexadecimaString(tcpUnpackerPara.Buffer, tcpUnpackerPara.Offset, tcpUnpackerPara.DataLength);
                    Loger.Warn($"设备{NetHelper.GetIPString(((NetLinkInfo)tcpUnpackerPara.LinkInfo).RemoteEndPoint)}帧同步字0x{Convert.ToString(sync, 16)}与期望同步字0x{Convert.ToString(DATA_PACK_HEAD_SYNC, 16)}不匹配,查找同步字.数据:\r\n{hexStr}");

                    //发生丢包,查找下一个包起始位置
                    stream.Position = currentParseBeginPosition;//从此位置开始查找
                    if (this.FindPackgeBegin(unpackerPara, stream, reader, parseEndPosition))
                    {
                        currentParseBeginPosition = stream.Position;
                        Loger.Info("找到同步字,继续...");
                        //新包读取一个同步字
                        continue;
                    }
                    else
                    {
                        //未找到包起始位置
                        Loger.Warn("未找到同步字,等待收到下次数据再继续...");
                        return parseDataLength;
                    }
                }

                //表示本包的总长度，单位为字节，包括数据包管理头和数据区长度
                int dataPackLen = reader.ReadInt32();
                if (currentParseBeginPosition + dataPackLen > parseEndPosition)
                {
                    //发生了分包
                    break;
                }


                int dataLen = dataPackLen - DATA_PACK_HEAD_SIZE;//读取数据,不含数据包头
                if (dataLen == 0)
                {
                    //数据包长度为0,说明是心跳包,忽略
                    currentParseBeginPosition = stream.Position;
                    continue;
                }

                if (this._useMode == UnpackerUseMode.Monopolize)
                {
                    if (this._bufferData == null || this._bufferData.Bytes != tcpUnpackerPara.Buffer)
                    {
                        this._bufferData = new DataSource(tcpUnpackerPara.Buffer, (int)reader.BaseStream.Position, dataLen);
                    }
                    else
                    {
                        this._bufferData.Update((int)reader.BaseStream.Position, dataLen);
                    }

                    if (this._args == null)
                    {
                        this._args = new DataOutputArgs(unpackerPara.LinkInfo, this._bufferData);
                    }
                    else
                    {
                        this._args.Update(unpackerPara.LinkInfo, this._bufferData);
                    }

                    args = this._args;
                }
                else
                {
                    var bufferData = new DataSource(tcpUnpackerPara.Buffer, (int)reader.BaseStream.Position, dataLen);
                    args = new DataOutputArgs(unpackerPara.LinkInfo, bufferData);
                }

                dataPackageEndPos = reader.BaseStream.Position + dataLen;
                base.OnRaiseOnReceivedData(unpackerPara.Client, args);
                reader.BaseStream.Position = dataPackageEndPos;
                currentParseBeginPosition = stream.Position;
            }

            parseDataLength = (int)(currentParseBeginPosition - parseBeginPosition);

            if (stream.Position != currentParseBeginPosition)
            {
                stream.Position = currentParseBeginPosition;
            }

            return parseDataLength;
        }

        /// <summary>
        /// 查找包起始位置
        /// </summary>
        /// <param name="unpackerPara">分包参数</param>
        /// <param name="stream">数据流</param>
        /// <param name="reader">BinaryReader</param>
        /// <param name="ep">在流中的查找结束位置</param>
        /// <returns>找到新包位置返回true,否则返回false</returns>
        private bool FindPackgeBegin(IUnpackerPara unpackerPara, Stream stream, BinaryReader reader, long ep)
        {
            var pp = stream.Position;//查找起始位置
            var fep = ep - DATA_PACK_HEAD_SIZE;//查找结束位置
            uint sync;

            for (var p = pp; p <= fep; p++)
            {
                if (unpackerPara.Client.Disposed)
                {
                    break;
                }

                stream.Position = p;
                sync = reader.ReadUInt32();
                if (sync != DATA_PACK_HEAD_SYNC)
                {
                    //无效同步字,继续从下一个字节开始找
                    continue;
                }

                stream.Position = p;//还原到读取同步字之前的位置
                return true;
            }

            return false;
        }


        private static void GenerateSendBuffer()
        {
            byte[] buffer = new byte[28];
            using (var ms = new MemoryStream(buffer))
            {
                var witer = new BinaryWriter(ms);
                witer.Write(DATA_PACK_HEAD_SYNC);
                witer.Write(buffer.Length);

                byte value = 1;
                for (var i = ms.Position; i < buffer.Length; i++)
                {
                    witer.Write(value);
                    value++;
                }
                ms.Flush();
            }
            string hexStr = ConvertEx.BytesToHexadecimaString(buffer);
        }
    }
}
